#include "Enviroment.h"

using namespace DirectX;

Enviroment::Enviroment(void)
{
  selected = 0;
  selectionEnabled = false;
  selectionX = selectionY = 0;
  left = right = up = down = 0;
  whiteMove = true;
  turnLight = false;
}


Enviroment::~Enviroment(void)
{
}

void Enviroment::LoadContent()
{
  IRenderable* obj = 0;
  Mesh* mesh;
  CurrentContext ctx = CurrentContext::GetInstance();
  float correction = 0.0f;

  ctx.Chessboard->SetTranslation(0,0,0);
  ctx.Chessboard->Show();

  objectsToRender.push_back(ctx.Chessboard);

  for(int i = 0; i < Chess::Board::LEN; ++i)
  {
    for(int j = 0; j < Chess::Board::LEN; ++j)
    {
      obj = AssetManager::Load(Chess::FigureTypeEnum::FIELD);
      objectsToRender.push_back(obj);

      ctx.Chessboard->SetField(dynamic_cast<Chess::Field*>(obj), i,j);
      //obj->Show();      
    }
  }

  ctx.Chessboard->Refresh();
  Chess::ColorEnum color;

  for(int i = 0; i < Chess::Board::LEN; ++i)
  {
    if(i > 1 && i < 6)
      continue;

    color = i < 2 ? Chess::ColorEnum::WHITE : Chess::ColorEnum::BLACK;

    for(int j = 0; j < Chess::Board::LEN; ++j)
    {
      mesh = 0;
      obj = 0;
      if(i == 1)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::PAWN, color);
      }
      else if(i == 6)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::PAWN, color);
      }

      if(i == 0 && (j == 0 || j == 7))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::ROOK, color);
      }
      else if(i == 7 && (j == 0 || j == 7))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::ROOK, color);      
      }

      if((i == 0) && (j == 2 || j == 5))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::BISHOP, color);
      }
      else if((i == 7) && (j == 2 || j == 5))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::BISHOP, color);
      }
      
      if(i == 0 && j == 3)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::KING, color);
      
      }
      else if(i == 7 && j == 4)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::KING, color);
      }

      if(i == 0 && j == 4)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::QUEEN, color);
      }
      else if(i == 7 && j == 3)
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::QUEEN, color);
      }

      if(i == 0 && (j == 1 || j == 6))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::KNIGHT, color);
        obj->SetRotation(0, -XM_PIDIV2, 0);
      }
      else if(i == 7 && (j == 1 || j == 6))
      {
        obj = AssetManager::Load(Chess::FigureTypeEnum::KNIGHT, color);
        obj->SetRotation(0,XM_PIDIV2,0);
      }


      if(obj != 0)
      {
        obj->SetTransparent(0.75f);

        mesh = dynamic_cast<DirectX::Mesh*>(obj);

        ctx.Chessboard->GetField(i,j)->SetFigure(dynamic_cast<Chess::IFigure*>(obj));
        Game::FieldMesh* f = dynamic_cast<Game::FieldMesh*>( ctx.Chessboard->GetField(i,j));
        XMMATRIX tmp = f->GetWorldMatrix();        

        XMFLOAT3 e = mesh->GetAABB()->Extents;
        XMFLOAT3 c = mesh->GetAABB()->Center;
        mesh->SetTranslation(tmp._41, tmp._42 + c.y + e.y , tmp._43);
        objectsToRender.push_back(mesh);
        mesh->Show();
      }
    }
  }

  // setting the light
  light = new Light();
  light->AmbientColor(XMFLOAT3(0.1,0.1,0.1));
  light->Distance(1);
  light->DiffuseColor(XMFLOAT3(1.0,1.0,1.0));
  light->SpecularColor(XMFLOAT3(0.4,0,0.4));
  light->Position(XMFLOAT3(0,10,0));  
}

void Enviroment::Render()
{
  float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f};

  unsigned int stride = sizeof(Vertex);
  unsigned int offset = 0;

  CurrentContext ctx = CurrentContext::GetInstance();

  ctx.DeviceContext->ClearRenderTargetView(ctx.BackBufferTarget, clearColor);
  ctx.DeviceContext->ClearDepthStencilView(ctx.DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );


  XMMATRIX view = XMMatrixTranslation(0,0,10);
  XMMATRIX proj = XMMatrixPerspectiveFovLH(XM_PIDIV4, 800.f/600, 0.01f, 100.f);

  ID3DX11EffectMatrixVariable* viewMatrix;
  viewMatrix = ctx.Effect->GetVariableByName( "ViewMatrix" )->AsMatrix( );
  viewMatrix->SetMatrix( ( float* ) &ctx.Camera->GetView() );

  ID3DX11EffectMatrixVariable* projMatrix;
  projMatrix = ctx.Effect->GetVariableByName( "ProjMatrix" )->AsMatrix( );
  projMatrix->SetMatrix( ( float* ) &ctx.Camera->GetProjection());

  ID3DX11EffectShaderResourceVariable* colorMap;
  colorMap = ctx.Effect->GetVariableByName( "ColorMap" )->AsShaderResource( );

  /*ID3DX11EffectShaderResourceVariable* secondColorMap;
  colorMap = ctx.Effect->GetVariableByName( "secondColorMap" )->AsShaderResource( );*/

  ID3DX11EffectSamplerVariable* colorMapSampler;
  colorMapSampler = ctx.Effect->GetVariableByName( "ColorSampler" )->AsSampler( );

  /*ID3DX11EffectScalarVariable* materialsCount;
  materialsCount = ctx.Effect->GetVariableByName("numMaterials")->AsScalar();*/

  ID3DX11EffectScalarVariable* alpha;
  alpha = ctx.Effect->GetVariableByName("Transparent")->AsScalar();

  ID3DX11EffectScalarVariable* texCount;
  texCount = ctx.Effect->GetVariableByName("TexCount")->AsScalar();

  ID3DX11EffectMatrixVariable* worldMatrix;

  ID3DX11EffectTechnique* multiTexTechnique;
  multiTexTechnique = ctx.Effect->GetTechniqueByName( "Alpha" );

  D3DX11_TECHNIQUE_DESC techDesc;
  multiTexTechnique->GetDesc( &techDesc );

  ctx.DeviceContext->IASetInputLayout( ctx.InputLayout );

  ID3DX11EffectVectorVariable* cameraPos;

  // setting light
  ID3DX11EffectVectorVariable* lightPos = ctx.Effect->GetVariableByName("LightPosition")->AsVector();
  ID3DX11EffectVectorVariable* lightDiffuse = ctx.Effect->GetVariableByName("LightDiffuseColor")->AsVector();
  ID3DX11EffectVectorVariable* lightSpecular = ctx.Effect->GetVariableByName("LightSpecularColor")->AsVector();
  ID3DX11EffectVectorVariable* lightAmbient = ctx.Effect->GetVariableByName("LightAmbientColor")->AsVector();
  ID3DX11EffectScalarVariable* lightSquaredDistance = ctx.Effect->GetVariableByName("LightSquaredDistance")->AsScalar();  
  ID3DX11EffectScalarVariable* lightTurnOn = ctx.Effect->GetVariableByName("LightTurnOn")->AsScalar();

  lightTurnOn->SetBool(turnLight);


  lightPos->SetFloatVector((float*) &light->Position());
  lightDiffuse->SetFloatVector((float*) &light->DiffuseColor());
  lightSpecular->SetFloatVector((float*) &light->SpecularColor());
  lightAmbient->SetFloatVector((float*) &light->AmbientColor());
  lightSquaredDistance->SetFloat(light->DistanceSquared());
  //

  // material
  ID3DX11EffectVectorVariable* materialDiffuse = ctx.Effect->GetVariableByName("DiffuseColor")->AsVector();
  ID3DX11EffectVectorVariable* materialEmissive = ctx.Effect->GetVariableByName("EmmisiveColor")->AsVector();
  ID3DX11EffectVectorVariable* materialSpecular = ctx.Effect->GetVariableByName("SpecularColor")->AsVector();
  ID3DX11EffectScalarVariable* materialSpecPower = ctx.Effect->GetVariableByName("SpecularPower")->AsScalar();  
  //
  unsigned n;
  TextureDesc* texDesc, *texDesc2;
  ID3D11ShaderResourceView* test[2];

  for(int i = 0; i < objectsToRender.size(); ++i)
  {
    if(objectsToRender[i]->Visible())
    {
      RenderDesc* renderDesc = objectsToRender[i]->GetRenderData();
      n = renderDesc->TotalMaterials >= 2 ? 2 : renderDesc->TotalMaterials;
      //materialsCount->SetInt(n);

      if(n == 2)
      {
        texDesc = TextureManager::GetInstance().GetTexture(renderDesc->Materials[0].TextureName);
        texDesc2 =TextureManager::GetInstance().GetTexture(renderDesc->Materials[1].TextureName);
        test[0] = texDesc->ColorMap;
        test[1] = texDesc2->ColorMap;

        colorMap->SetResourceArray(test, 0, 2);
        colorMapSampler->SetSampler( 0, texDesc->ColorMapSampler );   
      }
      else if(n == 1)
      {
        texDesc = TextureManager::GetInstance().GetTexture(renderDesc->Materials[0].TextureName);
        test[0] = texDesc->ColorMap;
        test[1] = NULL;

        colorMap->SetResourceArray(test, 0, 2);
        colorMapSampler->SetSampler( 0, texDesc->ColorMapSampler );   
      }
      else
      {
        n = 0;
      }

      texCount->SetInt(n);
      
      materialDiffuse->SetFloatVector((float*) &renderDesc->Materials[0].Diffuse);
      materialEmissive->SetFloatVector((float*) &renderDesc->Materials[0].Ambient);
      materialSpecular->SetFloatVector((float*) &renderDesc->Materials[0].Specular);
      materialSpecPower->SetFloat(renderDesc->Materials[0].Shininess);

      worldMatrix = ctx.Effect->GetVariableByName("WorldMatrix")->AsMatrix();
      worldMatrix->SetMatrix((float*) &(objectsToRender[i]->GetWorldMatrix()));

      ctx.DeviceContext->IASetIndexBuffer( renderDesc->IndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
      ctx.DeviceContext->IASetVertexBuffers(0, 1, &renderDesc->VertexBuffer, &stride, &offset);      

      ctx.DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

      alpha->SetFloat(objectsToRender[i]->Transparent());



      for( unsigned int p = 0; p < techDesc.Passes; p++ )
      {
        ID3DX11EffectPass* pass = multiTexTechnique->GetPassByIndex( p );

        if( pass != 0 )
        {
          pass->Apply( 0, ctx.DeviceContext);

          if(renderDesc->TotalIndices > 0)
            ctx.DeviceContext->DrawIndexed(renderDesc->TotalIndices,0,0);
          else
            ctx.DeviceContext->Draw(renderDesc->TotalVertices,0);
        }
      }
    }
  }


  ctx.SwapChain->Present(0,0);
}

void Enviroment::Update(float dt)
{
  double lim = 0.065;

  CurrentContext ctx = CurrentContext::GetInstance();
  HRESULT result;
  if(FAILED(result = ctx.KeyboardDevice->GetDeviceState(sizeof(ctx.KeyboardKeys), (LPVOID) &ctx.KeyboardKeys)))
  {
    if(result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
      ctx.KeyboardDevice->Acquire();
  }

  if(FAILED(result = ctx.MouseDevice->GetDeviceState(sizeof(ctx.MouseState), (LPVOID) &ctx.MouseState)))
  {
    
    if(result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
    {      
      ctx.MouseDevice->Acquire();
      result = ctx.MouseDevice->GetDeviceState(sizeof(ctx.MouseState), (LPVOID) &ctx.MouseState);
    }
  }

  ctx.MousePosX += ctx.MouseState.lX;
  ctx.MousePosY += ctx.MouseState.lY;
  ctx.MouseWheel += ctx.MouseState.lZ;

  if(ctx.MousePosX < 0) ctx.MousePosX = 0;
  else if(ctx.MousePosX > ctx.Width) ctx.MousePosX = ctx.Width;

  if(ctx.MousePosY < 0) ctx.MousePosY = 0;
  else if(ctx.MousePosY > ctx.Height) ctx.MousePosY = ctx.Height;

  if(GetAsyncKeyState(VK_ESCAPE))
  {
    PostQuitMessage(0);
  }

  CameraDevice* cam = ctx.Camera;

  if(KEYDOWN(ctx.KeyboardKeys, DIK_SPACE) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_SPACE))
  {
    if(selectionEnabled)
    {
      if(reachable.size() == 0)
      {
        reachable = ctx.Chessboard->GetReachableFields(selectionX, selectionY, whiteMove ? Chess::ColorEnum::WHITE : Chess::ColorEnum::BLACK); 
        selectedReachable = 0;
      }
    }
  }
  if(KEYDOWN(ctx.KeyboardKeys, DIK_Z) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_Z))
  {
    if(selectionEnabled && reachable.size() > 0)
    {
      Chess::Field* f = ctx.Chessboard->GetField(reachable[selectedReachable].x, reachable[selectedReachable].y);
      if(f->Occupied())
      {
        dynamic_cast<DirectX::Mesh*>(f->GetFigure())->Hide();
      }

      Chess::Field* nf = ctx.Chessboard->GetField(selectionX, selectionY);
      f->SetFigure(nf->GetFigure());
      nf->SetFigure(NULL);

      selectionX = reachable[selectedReachable].x;
      selectionY = reachable[selectedReachable].y;
      reachable.clear();

      whiteMove = !whiteMove;
      ctx.Chessboard->Refresh();
    }
  }
  else if(KEYDOWN(ctx.KeyboardKeys, DIK_X) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_X))
  {
    if(selectionEnabled && reachable.size() > 0)
      reachable.clear();
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_W) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_W))
    cam->MoveForward(1 * dt);

  if(KEYDOWN(ctx.KeyboardKeys, DIK_S) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_S))
    cam->MoveForward(-1 * dt);

  if(KEYDOWN(ctx.KeyboardKeys, DIK_A) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_A))
    cam->Strafe(-10 * dt);

  if(KEYDOWN(ctx.KeyboardKeys, DIK_D) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_D))
    cam->Strafe(10 * dt);

  if(KEYDOWN(ctx.KeyboardKeys, DIK_V) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_V))
  {
    ID3D11RasterizerState* state;
    ctx.DeviceContext->RSGetState(&state);
    D3D11_RASTERIZER_DESC desc;
    state->GetDesc(&desc);
    desc.FillMode = D3D11_FILL_WIREFRAME;

    ID3D11RasterizerState* newState;
    if(SUCCEEDED(ctx.Device->CreateRasterizerState(&desc, &newState)))
    {
      state->Release();
      ctx.DeviceContext->RSSetState(newState);
    }    
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_C) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_C))
  {
    ID3D11RasterizerState* state;
    ctx.DeviceContext->RSGetState(&state);
    D3D11_RASTERIZER_DESC desc;
    state->GetDesc(&desc);
    desc.FillMode = D3D11_FILL_SOLID;

    ID3D11RasterizerState* newState;
    if(SUCCEEDED(ctx.Device->CreateRasterizerState(&desc, &newState)))
    {
      state->Release();
      ctx.DeviceContext->RSSetState(newState);
    }    
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_P) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_P))
  {
    selectionEnabled = true;
  }
  else if(KEYDOWN(ctx.KeyboardKeys, DIK_L) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_L))
  {
    selectionEnabled = false;
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_UP) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_UP))
  {
    if(reachable.size() == 0)
    {
      up += dt;
      if(selectionEnabled && up >= lim)
      {
        if(++selectionX >= Chess::Board::LEN)
          selectionX = Chess::Board::LEN - 1;
        up = 0;
      }
    }
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_DOWN) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_DOWN))
  {
    if(reachable.size() == 0)
    {
      down += dt;
      if(selectionEnabled && down >= lim)
      {
        if(--selectionX <= 0)
          selectionX = 0;
        down = 0;
      }
    }
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_LEFT) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_LEFT))
  {
    left += dt;
    if(selectionEnabled && left >= lim)
    {
      if(reachable.size() == 0)
      {
        if(--selectionY <= 0)
          selectionY = 0;      
      }
      else
      {
        if(--selectedReachable <= 0)
          selectedReachable = 0;
      }
      left = 0;
    }
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_RIGHT) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_RIGHT))
  {
    right += dt;
    if(selectionEnabled && right >= lim)
    {
      if(reachable.size() == 0)
      {
        if(++selectionY >= Chess::Board::LEN)
          selectionY = Chess::Board::LEN - 1;
      }
      else
      {
        if(++selectedReachable >= reachable.size())
          selectedReachable = reachable.size() - 1;
      }
      right = 0;
    }
  }

  if(BUTTONDOWN(ctx.MouseState, 1) && !BUTTONDOWN(ctx.PrevMouseState, 1))
  {
    cam->SetYaw(ctx.MouseState.lX / 100.0f);
    cam->SetPitch(ctx.MouseState.lY / 100.0f);
  }

  if(KEYDOWN(ctx.KeyboardKeys, DIK_O) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_O))
  {
    turnLight = false;
  }
  else if(KEYDOWN(ctx.KeyboardKeys, DIK_I) && !KEYDOWN(ctx.PrevKeyboardKeys, DIK_I))
  {
    turnLight = true;
  }
  //file << m_mouseState.lX << "\t" << m_mouseState.lY << std::endl;

  memcpy( ctx.PrevKeyboardKeys, ctx.KeyboardKeys, sizeof( ctx.KeyboardKeys ) );
  memcpy( &ctx.PrevMouseState, &ctx.MouseState, sizeof( ctx.MouseState) );

  cam->Update();

  if(selectionEnabled)
  {
    ctx.Chessboard->DeselectAll(Chess::FieldStatusEnum::CANREACH);
    for(unsigned i = 0; i < reachable.size(); ++i)
    {
      ctx.Chessboard->ChangeStatus(Chess::FieldStatusEnum::CANREACH, reachable[i].x, reachable[i].y);
    }
    ctx.Chessboard->DeselectAll();
    if(reachable.size() == 0)
      ctx.Chessboard->Select(selectionX, selectionY);
    else
      ctx.Chessboard->Select(reachable[selectedReachable].x, reachable[selectedReachable].y);
  }
  else
  {
    ctx.Chessboard->DeselectAll(Chess::FieldStatusEnum::NONE);
  }

}
